package input

import (
	"fmt"
	"reflect"
	"strconv"

	"gitee.com/knowgo/ego/core/function"
)

func parsePrimitive(argType *function.ArgumentTypeInfo, data interface{}) (reflect.Value, error) {
	val, err := doParsePrimitive(argType, data)
	if err != nil {
		return val, err
	}
	// 验证合法性
	realVal := reflect.Indirect(val)
	for _, v := range argType.Validators {
		if err := v.IsValidate(argType.Name, realVal); err != nil {
			return val, err
		}
	}
	return val, nil
}

func doParsePrimitive(argType *function.ArgumentTypeInfo, data interface{}) (reflect.Value, error) {
	var info = argType.TypeInfo
	var val = reflect.New(info.ReferenceType)
	if data == nil {
		// 数据是 nil ，返回空
		return reflect.Zero(info.ReferenceType), nil
	}

	// o.CanSet()
	switch info.ReferenceType.Kind() {
	case reflect.Int,
		reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return parseInt(val, argType, data)

	case reflect.Float64, reflect.Float32:
		return parseFloat(val, argType, data)
	case reflect.String:
		str, ok := data.(string)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect string", argType.Name)
		}
		// if argType.TypeInfo.IsPtr {
		val.Elem().SetString(str)
		// } else {
		// 	val.Elem().SetString(str)
		// }
		return val, nil
	case reflect.Bool:
		return parseBool(val, argType, data)
	case reflect.Complex128, reflect.Complex64:
		return parseComplex128(val, argType, data)
	}
	return val, fmt.Errorf("unsurport data type (%s)", argType.Name)
}

func parseComplex128(val reflect.Value, argType *function.ArgumentTypeInfo, data interface{}) (reflect.Value, error) {
	var (
		complex complex128
		err     error
		ok      bool
	)
	if argType.JsonValueIsString {
		// string to int
		str, ok := data.(string)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect string(%s)", argType.Name, argType.Type)
		}
		complex, err = strconv.ParseComplex(str, 128)
		if err != nil {
			return val, fmt.Errorf("[%s] invalid data value (%s), expect string(%s)", argType.Name, str, argType.Type)
		}
	} else {
		complex, ok = data.(complex128)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect %s", argType.Name, argType.Type)
		}
	}

	// if argType.TypeInfo.IsPtr {
	val.Elem().SetComplex(complex)
	// } else {
	// 	val.SetComplex(complex)
	// }
	return val, nil
}

func parseBool(val reflect.Value, argType *function.ArgumentTypeInfo, data interface{}) (reflect.Value, error) {
	var (
		bVal bool
		err  error
		ok   bool
	)
	if argType.JsonValueIsString {
		// string to int
		str, ok := data.(string)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect string(%s)", argType.Name, argType.Type)
		}
		bVal, err = strconv.ParseBool(str)
		if err != nil {
			return val, fmt.Errorf("[%s] invalid data value (%s), expect string(%s)", argType.Name, str, argType.Type)
		}
	} else {
		bVal, ok = data.(bool)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect %s", argType.Name, argType.Type)
		}
	}

	// if argType.TypeInfo.IsPtr {
	val.Elem().SetBool(bVal)
	// } else {
	// 	val.SetBool(bVal)
	// }
	return val, nil
}

func parseFloat(val reflect.Value, argType *function.ArgumentTypeInfo, data interface{}) (reflect.Value, error) {
	var (
		floatVal float64
		err      error
		ok       bool
	)
	if argType.JsonValueIsString {
		// string to int
		str, ok := data.(string)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect string(%s)", argType.Name, argType.Type)
		}
		floatVal, err = strconv.ParseFloat(str, 64)
		if err != nil {
			return val, fmt.Errorf("[%s] invalid data value(%s), expect string(%s)", argType.Name, str, argType.Type)
		}
	} else {
		floatVal, ok = data.(float64)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect %s", argType.Name, argType.Type)
		}
	}

	// if argType.TypeInfo.IsPtr {
	val.Elem().SetFloat(floatVal)
	// } else {
	// 	val.SetFloat(floatVal)
	// }
	return val, nil
}

func parseInt(val reflect.Value, argType *function.ArgumentTypeInfo, data interface{}) (reflect.Value, error) {
	var (
		intVal int64
		err    error
		ok     bool
	)

	if argType.JsonValueIsString {
		str, ok := data.(string)
		if !ok {
			return val, fmt.Errorf("[%s] Invalid data type, expect string(%s)", argType.Name, argType.Type)
		}
		intVal, err = strconv.ParseInt(str, 10, 64)
		if err != nil {
			// 字符转整数错误
			return val, fmt.Errorf("[%s] invalid data value (%s), expect string(%s)", argType.Name, str, argType.Type)
		}
	} else {
		intVal, ok = data.(int64)
		if !ok {
			Float, ok := data.(float64)
			if !ok {
				// 数据类型错误
				return val, fmt.Errorf("[%s] Invalid data type, expect %s", argType.Name, argType.Type)
			}
			intVal = int64(Float)
		}
	}
	// if argType.TypeInfo.IsPtr {
	val.Elem().SetInt(intVal)
	// } else {
	// 	val.SetInt(intVal)
	// }
	return val, nil
}
